package com.fzfnn.scrapbook.service.impl;

import com.fzfnn.scrapbook.entity.LoginLog;
import com.fzfnn.scrapbook.entity.User;
import com.fzfnn.scrapbook.mapper.JournalMapper;
import com.fzfnn.scrapbook.mapper.LoginLogMapper;
import com.fzfnn.scrapbook.mapper.UserMapper;
import com.fzfnn.scrapbook.service.UserService;
import com.fzfnn.scrapbook.util.EmailUtil;
import com.fzfnn.scrapbook.util.IpUtil;
import com.fzfnn.scrapbook.util.PasswordUtil;
import com.fzfnn.scrapbook.util.R;
import com.fzfnn.scrapbook.vo.PersonalhomepageVo;
import com.fzfnn.scrapbook.vo.UserVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.security.SecureRandom;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Base64;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Component
public class UserServiceImpl implements UserService {

    @Autowired
    UserMapper userMapper;
    @Autowired
    private LoginLogMapper loginLogMapper;
    @Autowired
    private IpUtil ipUtil;

    @Autowired
    private RedisTemplate<String, String> redisMail;
    @Autowired
    private JournalMapper journalMapper;

    @Override
    public R register(String email, String password, String code) {
        if (code == null || code.trim().isEmpty()) {
            return R.errorMsg("验证码不能为空");
        }
        String redisCode = redisMail.opsForValue().get(email);
        if (redisCode == null) {
            return R.errorMsg("验证码已过期，请重新获取");
        }
        if (!redisCode.equalsIgnoreCase(code.trim())) {
            return R.errorMsg("验证码错误");
        }
        if (userMapper.selectUserByEmail(email) != null) {
            return R.errorMsg("该邮箱已被注册");
        }
        if (!isStrongPassword(password)) {
            return R.errorMsg("密码过于简单，需包含大小写字母、数字，长度不少于8位");
        }
        User user = new User();
        user.setPassword(PasswordUtil.encryptPassword(password));
        user.setEmail(email);
        user.setCreatedAt(Timestamp.valueOf(LocalDateTime.now(ZoneId.of("Asia/Shanghai"))));
        userMapper.insertUser(user);
        redisMail.delete(email);
        return R.success("注册成功", 0);
    }

    @Override
    public R getCode(String email) {
        if (!isValidEmail(email)) {
            return R.errorMsg("无效的邮箱格式，请重新输入！");
        }
        // 限制频繁获取验证码
        String limitKey = "limit:" + email;
        if (redisMail.hasKey(limitKey)) {
            return R.errorMsg("请勿频繁获取验证码，请60秒后再试");
        }
        SecureRandom random = new SecureRandom();
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < 6; i++) {
            builder.append(random.nextInt(10));
        }
        String code = builder.toString();
        try {
            EmailUtil.sendEmail(email, "验证码", "您的验证码是：" + code + "，请在5分钟内使用");
            redisMail.opsForValue().set(email, code, 5, TimeUnit.MINUTES);
            redisMail.opsForValue().set(limitKey, "1", 60, TimeUnit.SECONDS);
            return R.successMsg("验证码已发送到您的邮箱，请查收");
        } catch (Exception e) {
            return R.errorMsg("验证码发送失败或存储失败");
        }
    }

    @Override
    public R login(String email, String password, HttpServletRequest request) {
        if (!isValidEmail(email)) {
            return R.errorMsg("无效的邮箱格式，请重新输入！");
        }

        String lockKey = "login:lock:" + email;
        if (redisMail.hasKey(lockKey)) {
            return R.errorMsg("登录失败次数过多，请5分钟后再试");
        }

        String storedHash = userMapper.checkPasswordByEmail(email);
        if (storedHash == null) {
            return R.errorMsg("该邮箱尚未注册");
        }

        boolean result = PasswordUtil.checkPassword(password, storedHash);
        if (!result) {
            String failKey = "login:fail:" + email;
            Long failCount = redisMail.opsForValue().increment(failKey);
            redisMail.expire(failKey, 5, TimeUnit.MINUTES);

            if (failCount != null && failCount >= 5) {
                redisMail.opsForValue().set(lockKey, "1", 5, TimeUnit.MINUTES);
                return R.errorMsg("密码错误次数过多，账户已暂时锁定");
            }
            return R.errorMsg("密码错误");
        }

        // 登录成功，清除失败记录
        redisMail.delete("login:fail:" + email);
        redisMail.delete(lockKey);

        User user = userMapper.selectUserByEmail(email);

        // 检查该用户是否已经登录
        String tokenKey = "user:" + user.getUserId() + ":token";
        if (redisMail.hasKey(tokenKey)) {
            return R.errorMsg("该账户已经在其他地方登录");
        }

        // 获取 IP 和城市信息
        String ip = IpUtil.getClientIp(request);
        if ("0:0:0:0:0:0:0:1".equals(ip) || "::1".equals(ip)) {
            ip = "127.0.0.1";
        }
        String city = IpUtil.getCityInfo(ip);

        LoginLog log = new LoginLog();
        log.setUserId(Long.valueOf(user.getUserId()));
        log.setEmail(email);
        log.setIpAddress(ip);
        log.setLoginAddress(city);
        log.setLoginTime(Timestamp.valueOf(LocalDateTime.now()));
        log.setMessage("登录成功");
        loginLogMapper.insertLog(log);
        String token = generateToken(user.getUserId());
        String tokenRedisKey = "token:" + token;
        redisMail.opsForValue().set(tokenRedisKey, user.getUserId().toString(), 1, TimeUnit.HOURS);
        redisMail.opsForValue().set(tokenKey, token, 1, TimeUnit.HOURS);

        // 存储登录信息（避免和 token 类型冲突）
        String loginInfoKey = "user:" + user.getUserId() + ":loginInfo";
        redisMail.delete(loginInfoKey); // 保证不会类型冲突
        redisMail.opsForHash().put(loginInfoKey, "ip", ip);
        redisMail.opsForHash().put(loginInfoKey, "city", city);
        redisMail.opsForHash().put(loginInfoKey, "last_login", LocalDateTime.now().toString());
        redisMail.expire(loginInfoKey, 1, TimeUnit.HOURS);
        String cityUserOnlineKey = "city:online:" + city + ":" + user.getUserId();
        redisMail.opsForValue().set(cityUserOnlineKey, user.getUserId().toString(), 1, TimeUnit.HOURS);

        return R.success("登录成功", token);
    }


    @Override
    public R getCurrentUser(String token) {
        String userId = redisMail.opsForValue().get("token:" + token);
        if (userId == null) {
            return R.errorMsg("登录信息已过期，请重新登录");
        }
        User user = userMapper.selectUserByuserid(Integer.parseInt(userId));
        return R.success("获取成功", user);
    }
    @Override
    public R logout(String token) {
        if (token == null || token.trim().isEmpty()) {
            return R.errorMsg("token不能为空");
        }

        String tokenKey = "token:" + token;
        String userId = redisMail.opsForValue().get(tokenKey);
        if (userId == null) {
            return R.errorMsg("登录信息已过期，请重新登录");
        }

        // 先获取城市（注意从 loginInfo 获取）
        String loginInfoKey = "user:" + userId + ":loginInfo";
        String city = (String) redisMail.opsForHash().get(loginInfoKey, "city");

        // 清除所有相关缓存
        redisMail.delete(tokenKey);
        redisMail.delete("user:" + userId + ":token");
        redisMail.delete(loginInfoKey);

        // 删除用户城市在线状态 key
        if (city != null) {
            String cityOnlineKey = "city:online:" + city + ":" + userId;
            redisMail.delete(cityOnlineKey);
        }

        return R.successMsg("登出成功");
    }



    @Override
    public R updateUserInfo(String username, String password, String signature, String token) {
        // 1. Token 校验
        if (token == null || token.trim().isEmpty()) {
            return R.errorMsg("token不能为空");
        }

        // 2. 从 Redis 获取 userId
        String userIdStr = redisMail.opsForValue().get("token:" + token);
        if (userIdStr == null) {
            return R.errorMsg("登录信息已过期，请重新登录");
        }

        Integer userId;
        try {
            userId = Integer.parseInt(userIdStr);
        } catch (NumberFormatException e) {
            return R.errorMsg("无效的用户ID");
        }

        // 3. 查询数据库中的用户信息
        User dbUser = userMapper.selectUserByuserid(userId);
        if (dbUser == null) {
            return R.errorMsg("用户不存在");
        }

        boolean isModified = false;
        boolean isPasswordUpdated = false;

        // 4. 更新用户名
        if (username != null && !username.trim().isEmpty() && !username.equals(dbUser.getUsername())) {
            dbUser.setUsername(username);
            isModified = true;
        }

        // 5. 更新密码
        if (password != null && !password.trim().isEmpty()) {
            if (!isStrongPassword(password)) {
                return R.errorMsg("密码过于简单，需包含大小写字母、数字，长度不少于8位");
            }
            dbUser.setPassword(PasswordUtil.encryptPassword(password));
            isModified = true;
            isPasswordUpdated = true;
        }

        // 6. 如果无字段修改，直接返回
        if (!isModified) {
            return R.successMsg("无更新内容");
        }

        // 7. 更新数据库
        userMapper.updateUser(dbUser);

        // 8. 密码更新 -> 清除 token
        if (isPasswordUpdated) {
            redisMail.delete("token:" + token);
            redisMail.delete("user:" + userId + ":token");
            return R.successMsg("密码更新成功，请重新登录");
        }

        return R.successMsg("用户信息更新成功");
    }


    @Override
    public R updateEmail(String email, String code, String token) {
        if (!isValidEmail(email)) {
            return R.errorMsg("无效的邮箱格式，请重新输入！");
        }
        // 验证码不能为空
        if (code == null || code.trim().isEmpty()) {
            return R.errorMsg("验证码不能为空");
        }
        // 从 Redis 获取对应的验证码
        String redisCode = redisMail.opsForValue().get(email);
        System.out.println(redisCode);
        if (redisCode == null) {
            return R.errorMsg("验证码已过期，请重新获取");
        }
        // 验证验证码是否正确
        if (!redisCode.equalsIgnoreCase(code.trim())) {
            return R.errorMsg("验证码错误");
        }
        // 验证通过后，获取用户ID
        String userIdStr = redisMail.opsForValue().get("token:" + token);
        if (userIdStr == null) {
            return R.errorMsg("登录信息已过期，请重新登录");
        }
        Integer userId = Integer.parseInt(userIdStr);
        // 查询用户信息
        User dbUser = userMapper.selectUserByuserid(userId);
        if (dbUser == null) {
            return R.errorMsg("用户不存在");
        }
        // 更新邮箱信息
        dbUser.setEmail(email);
        userMapper.updateEmail(dbUser);
        // 删除 Redis 中的验证码，确保验证码只能使用一次
        redisMail.delete(email);
        return R.successMsg("邮箱更新成功");
    }

    @Override
    public R loginByEmail(String email, String code) {
        if (!isValidEmail(email)) {
            return R.errorMsg("无效的邮箱格式，请重新输入！");
        }
        // 验证码不能为空
        if (code == null || code.trim().isEmpty()) {
            return R.errorMsg("验证码不能为空");
        }
        // 从 Redis 获取对应的验证码
        String redisCode = redisMail.opsForValue().get(email);
        System.out.println(redisCode);
        if (redisCode == null) {
            return R.errorMsg("验证码已过期，请重新获取");
        }
        // 验证验证码是否正确
        if (!redisCode.equalsIgnoreCase(code.trim())) {
            return R.errorMsg("验证码错误");
        }
        User user = userMapper.selectUserByEmail(email);
        String userTokenKey = "user:" + user.getUserId() + ":token";
        if (redisMail.hasKey(userTokenKey)) {
            return R.errorMsg("该账户已经在其他地方登录");
        }
        // 生成 token
        String token = generateToken(user.getUserId());
        // 存储 token 与用户关联
        String redisKey = "token:" + token;
        redisMail.opsForValue().set(redisKey, user.getUserId().toString(), 1, TimeUnit.HOURS);
        // 存储用户的 token 状态
        redisMail.opsForValue().set(userTokenKey, token, 1, TimeUnit.HOURS);
        redisMail.delete(email);
        return R.success("登录成功", token);
    }

    @Override
    public R forgetPassword(String email, String code, String newPassword) {
        if (!isValidEmail(email)) {
            return R.errorMsg("无效的邮箱格式，请重新输入！");
        }
        // 验证码不能为空
        if (code == null || code.trim().isEmpty()) {
            return R.errorMsg("验证码不能为空");
        }
        // 从 Redis 获取对应的验证码
        String redisCode = redisMail.opsForValue().get(email);
        System.out.println(redisCode);
        if (redisCode == null) {
            return R.errorMsg("验证码已过期，请重新获取");
        }
        // 验证验证码是否正确
        if (!redisCode.equalsIgnoreCase(code.trim())) {
            return R.errorMsg("验证码错误");
        }
        User user = userMapper.selectUserByEmail(email);
        if (user == null){
            return R.errorMsg("该邮箱未注册");
        }
        if (!isStrongPassword(newPassword)) {
            return R.errorMsg("密码过于简单，需包含大小写字母、数字，长度不少于8位");
        }
        String newpassword = PasswordUtil.encryptPassword(newPassword);
        userMapper.forgetPassword(email, newpassword);
        int userId= (userMapper.selectUserByEmail(email)).getUser_id();
        redisMail.delete(email);
        String token = redisMail.opsForValue().get("user:"+userId+":token");
        // 删除存储的 token
        redisMail.delete("token:" + token);
        // 删除与用户关联的 token 状态
        redisMail.delete("user:" + userId + ":token");
        return R.successMsg("密码重置成功，请重新登录");
    }

    @Override
    public User selectUserByid(Long id) {
        return userMapper.selectUserById(id);
    }

    @Override
    public String getImgUrl(Long id) {
        return userMapper.getImgUrl(id);
    }


    //邮箱地址验证
    public static boolean isValidEmail(String email) {
        String regex = "^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+$";
        return email != null && email.matches(regex);
    }
    //密码强度验证
    public static boolean isStrongPassword(String password) {
        if (password == null || password.length() < 8) return false;
        String pattern = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).{8,}$";
        return password.matches(pattern);
    }
    // 修改后的生成token方法
    private String generateToken(int userId) {
        try {
            String secretKey = "Y8#jQ2!9z$e@pLrA7cM1"; // 更安全的秘钥
            String data = userId + ":" + System.currentTimeMillis();
            Mac mac = Mac.getInstance("HmacSHA256");
            SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(), "HmacSHA256");
            mac.init(secretKeySpec);
            byte[] rawHmac = mac.doFinal(data.getBytes());
            return Base64.getEncoder().encodeToString(rawHmac); // 返回Base64编码的token
        } catch (Exception e) {
            throw new RuntimeException("Token生成失败", e);
        }
    }

    @Override
    public List<UserVo> getPrivateChatUsers(String token, Long receiverId) {
        String userId = redisMail.opsForValue().get("token:" + token);

        return userMapper.getTwoUserInfos(Long.valueOf(userId),  receiverId);
    }

    @Override
    public R parseToken(String token) {
        String userId = redisMail.opsForValue().get("token:" + token);
        return R.successMsg("刷新成功");
    }
    @Override
    public R personalHomepage(String token, Integer userIds) {
        // 获取当前登录用户 ID
        String userIdStr = redisMail.opsForValue().get("token:" + token);
        if (userIdStr == null) {
            return R.errorMsg("登录信息无效");
        }
        Long currentUserId;
        try {
            currentUserId = Long.parseLong(userIdStr);
        } catch (NumberFormatException e) {
            return R.errorMsg("用户ID格式错误");
        }
        // 处理 userIds 为 null 或 0 的情况：表示查看自己
        int targetUserId = (userIds == null || userIds == 0) ? currentUserId.intValue() : userIds;
        // 查询目标用户信息
        User targetUser = userMapper.selectUserById((long) targetUserId);
        if (targetUser == null) {
            return R.errorMsg("目标用户不存在");
        }

        PersonalhomepageVo personalHomepageVo = new PersonalhomepageVo();
        personalHomepageVo.setUserName(targetUser.getUsername());
        personalHomepageVo.setEmail(targetUser.getEmail());
        personalHomepageVo.setUserAvatar(userMapper.getImgUrl((long) targetUserId));
        personalHomepageVo.setUserId(targetUserId);
        personalHomepageVo.setSignature(targetUser.getSignature());

        // 判断是否是本人
        if (currentUserId.intValue() == targetUserId) {
            personalHomepageVo.setJournals(journalMapper.getJournalsWithUserIdbymyself(targetUserId));
        } else {
            personalHomepageVo.setJournals(journalMapper.getJournalsWithUserId(targetUserId));
        }

        return R.successData(personalHomepageVo);
    }
@Override
public void updateUserTags(String token) {
    String userId = redisMail.opsForValue().get("token:" + token);
        List<String> tags = journalMapper.selectTagsByUserActivity(Integer.valueOf(userId));

        Set<String> tagSet = new HashSet<>();
        for (String tagStr : tags) {
            if (tagStr != null && !tagStr.trim().isEmpty()) {
                // 逗号、中文逗号、分号、空格等多种分隔符拆分
                String[] splitTags = tagStr.split("[,，；;\\s]+");
                for (String tag : splitTags) {
                    if (!tag.trim().isEmpty()) {
                        tagSet.add(tag.trim());
                    }
                }
            }
        }

        // 多标签用英文逗号拼接保存
        String combinedTags = String.join(",", tagSet);
        userMapper.updateUserTag(Integer.valueOf(userId), combinedTags);
    }

}
